#!usr/bin/python
# -*- coding: utf8 -*-
encoding='utf8'
'''утф'''

from Source import Iter_plus
from Tokenize import Token
from Namespace import Namespace

## Start concatenation ##

class SomeDefinition:
    def __init__(self):
        pass

    def parse(self, tokens, namespace):
        t = tokens.current()
        if namespace.isType(t):
            f_func, f_var = False, False
            # Variable or Function
            tokens.pushPosition()
            tokens.pushPosition()
            f_func = self.isFunction(tokens, namespace)
            tokens.popPosition()
            f_var = self.isVariable(tokens, namespace)
            tokens.popPosition()
            if f_func:  self.defFunction(tokens, namespace)
            elif f_var: self.defVariable(tokens, namespace)
            else:
                print u'Unrecognized func/var definition:'
                print u'%s: %s' % (t.position(), t.text())
        elif namespace.isNamespace_keyword(t):
            # Typedef, Struct, Enum or Union
            if   t.text() == u'typedef': self.defTypedef(tokens, namespace)
            elif t.text() == u'struct':  self.defStruct( tokens, namespace)
            elif t.text() == u'enum':    self.defEnum(   tokens, namespace)
            elif t.text() == u'union':   self.defUnion(  tokens, namespace)
            else:
                print u'Unrecognized namespace definition:'
                print u'%s: %s' % (t.position(), t.text())
        else:
            print u'Unrecognized definition:'
            print u'%s: %s' % (t.position(), t.text())

    def isVariable(self, tokens, namespace):
        var = Variable(namespace)
        for t in tokens.byToken(True):
            if t.node_type == 'leaf':
                if t.type == 'oper':
                    if t.text() in u',;':
                        return True
            if not var.parse(t):
                return False
        return False

    def defVariable(self, tokens, namespace):
        var = Variable(namespace)
        for t in tokens.byToken(True):
            if t.node_type == 'leaf':
                if t.type == 'oper':
                    if t.text() == u';':
                        namespace.addVariable(var)
                        return
                    elif t.text() == u',':
                        namespace.addVariable(var)
                        old = var
                        var = Variable(namespace)
                        for vt in old.var_type:
                            var.parse(vt)
            if not var.parse(t):
                return

    def isFunction(self, tokens, namespace):
        func = self.defFunction(tokens, namespace, True)
        if func:
            return True
        return False

    def defFunction(self, tokens, namespace, do_not_add = False):
        func = Function(namespace)
        if func.parse(tokens):
            if do_not_add:
                return True
            else:
                namespace.addFunction(func)

    def defStruct(self, tokens, namespace):
        if not namespace.isType(tokens.next()):
            struct = Struct(namespace)
            struct.parse(tokens)
        else:
            # Definition of previously declared struct type
            tokens.move(1)
            # Be carefull with deadlocks...
            self.parse(tokens, namespace)

    def defEnum(self, tokens, namespace):
        if not namespace.isType(tokens.next()):
            enum = Enum(namespace)
            enum.parse(tokens)
        else:
            pass
            #tokens.move(1)

    def defUnion(self, tokens, namespace):
        tokens.move(1)

    def defTypedef(self, tokens, namespace):
        td = Typedef(namespace)
        if td.parse(tokens):
            namespace.addType(td)

def evalNumber(token_iter, namespace):
    eval_str = u''
    some_token = None
    for token in token_iter.byToken():
        if token.node_type == 'leaf':
            some_token = token
            if token.type == 'decimal':
                eval_str+= token.text()
            elif token.type == 'octuple':
                eval_str+= unicode( int(token.text(), 8) )
            elif token.type == 'hexadecimal':
                eval_str+= unicode( int(token.text(), 16) )
            elif token.type == 'oper':
                eval_str+= token.text()
            elif token.type == 'ident':
                if token.text() == u'sizeof':
                    token_iter.move(1)
                    assert( token_iter.current().type == 'expression' ), token_iter.current().node_type
                    sizeof = evalSizeof(token_iter.current().children, namespace)
                    eval_str+= unicode(sizeof)
                else:
                    if namespace.isConstant(token):
                        var = namespace.getVariable(token)
                        for t in var.initialization:
                            eval_str+= t.text()
                    else:
                        print u'* %s' % (token.text())
    if not len(eval_str):
        return u'ERROR'
    try:
        result = int(eval(eval_str))
    except SyntaxError, e:
        print u'Syntax Error in evaluation:'
        print u'"%s"' % (eval_str)
        print u'Some additional output:'
        print u'"%s"' % (some_token.origin.debug())
        raise e
    return result

def evalSizeof(token_iter, namespace):
    type_list = []
    for token in token_iter.byToken():
        if token.text() not in u'()':
            type_list.append(token)
    t = Type(namespace)
    if t.parse(type_list):
        return t.size()
    else:
        print u'Error: evalSizeof() not type:'
        print u' '.join([a.text() for a in token_iter.get()])

class Variable(object):
    def __init__(self, namespace):
        self.name           = u''
        self.var_type       = []
        self.dimensions     = []
        self.modifiers      = []
        self.initialization = []
        self.namespace      = namespace
        self.own_size       = 0
        # Flags:
        self.name_defined = False
        self.type_defined = False
        self.hard_sized   = False
        self.initializing = False
        self.no_more      = False
        self.pass_all     = False
        self.origin       = None
        self.loud_mode    = False
        self.dim_by_ini   = False # char a[] = "Hello";

    def parse(self, token):
        if self.loud_mode:
            if type(token) == Token:
                print '%s LOUD (%s): %d %s' % (token.position(), self.name, self.dim_by_ini, token.text())
        if self.pass_all:
            return True
        if self.no_more:
            print u'Error: Variable.parse: Can\'t take no more, but "%s" passed' % (token.text())
        if token.node_type == 'node':
            if self.name_defined:
                if self.initializing:
                    self.initialization.append(token)
                else:
                    if token.type == 'array':
                        pass_to_eval = Iter_plus()
                        have_tokens = False
                        for t in token.children.byToken():
                            if t.text() not in u'[]':
                                pass_to_eval.append(t)
                                pass_to_eval.move(1)
                                have_tokens = True
                        if have_tokens:
                            evaled = evalNumber(pass_to_eval, self.namespace)
                            self.dimensions.append( [evaled] )
                        else:
                            if not self.dim_by_ini:
                                self.dim_by_ini = True
                            else:
                                print token.children.first().origin.debug()
                                raise SyntaxError("More than 1 undefined dimension.")
                    else:
                        # It must be function definition
                        #print 'WTF node +n not array:', token.text()
                        return False
            else:
                if token.type == 'expression':
                    # Pointer to function ex: void (*func_ozu)(void);
                    for t in token.children.byToken():
                        if not t.text() in u'()':
                            self.var_type.append(t)
                    self.name = self.var_type[-1].text()
                    self.origin = self.var_type[-1].origin
                    del self.var_type[-1]
                    self.name_defined = True
                    self.pass_all     = True
                    self.own_size     = self.namespace.type_sizes[u'int']
                    self.hard_sized   = True
                    return True
                else:
                    print 'WTF node -n:', token.text()
                    return False
        elif token.node_type == 'leaf':
            if self.initializing:
                self.initialization.append(token)
            elif self.hard_sized:
                self.own_size = int(token.text()) / 8.
                self.no_more = True
            elif self.name_defined:
                if token.type == 'oper':
                    if token.text() == u'=':
                        self.initializing = True
                    elif token.text() == u':':
                        self.hard_sized = True
                    else:
                        print '%s +n !=: %s' % (token.position(), token.text())
                        return False
                else:
                    print '%s +n !oper (%s): %s' % (token.position(), self.name, token.text())
                    return False
            else:
                if token.type == 'ident':
                    if self.namespace.isType(token):
                        self.var_type.append(token)
                        self.type_defined = True
                    elif self.namespace.isIdent(token):
                        if self.type_defined:
                            self.name = token.text()
                            self.origin = token.origin
                            self.name_defined = True
                        else:
                            print '%s: vaiable type undefined "%s"' % (
                                    token.position(), token.text() )
                            return False
                    else:
                        print 'WTF -n -i nor kw, nor ident:', token.text()
                elif token.type == 'oper':
                    if self.type_defined:
                        if token.text() in u'*&':
                            self.modifiers.append(token)
                        elif token.text() in u',;':
                            pass
                        else:
                            print '%s: bad modifier "%s"' % (
                                    token.position(), token.text() )
                            return False
                    else:
                        print '%s: type not defined "%s"' % (
                                token.position(), token.text() )
                        return False
                elif token.type == 'ws':
                    return True
                else:
                    print 'WTF -n -i unrecognized:'
                    print u'%s: %s' % ( token.position(), token.text() )
        return True

    def getOrigin(self):
        return self.origin

    def size(self):
        if self.own_size == 0:
            for m in self.modifiers:
                if m.text() == u'*':
                    return self.namespace.type_sizes[u'int']
            sizes = []
            for v in self.var_type:
                if hasattr(v, 'size'):
                    sizes.append(v.size())
                else:
                    sizes.append(self.namespace.getSize(v))
            actual = reduce(max, sizes)
            for d in self.dimensions:
                val = d[0]
                if val > 0:
                    actual*= val
        else:
            actual = self.own_size
        return actual

    def varType(self):
        output = u' '.join([v.text() for v in self.var_type])
        return output

    def isArray(self):
        if len(self.dimensions):
            return True
        return False

    def expandArray(self):
        count = 1
        for a in self.dimensions:
            count*= a[0]
        right_count = [1 for a in range(len(self.dimensions))]
        for i1, d1 in enumerate(self.dimensions):
            for i2, d2 in enumerate(self.dimensions):
                if i2 < i1:
                    right_count[i2]*= self.dimensions[i1][0]
        result = [[0 for a in range(len(self.dimensions))] for i in range(count)]
        for ri in range(count):
            for nd, d in enumerate(self.dimensions):
                result[ri][nd] = (ri / right_count[nd]) % d[0]
        return result

    def nameFor_map(self):
        output = u''
        for m in self.modifiers:
            output+= m.text()
        output+= self.name
        if len(self.dimensions):
            for d in self.dimensions:
                output+= u'['
                for t in d:
                    output+= unicode(t) # t.text()
                output+= u']'
        return output

    def text(self):
        output = u''
        for v in self.var_type:
            output+= v.text() + u' '
        for m in self.modifiers:
            output+= m.text()
        output+= self.name
        if len(self.dimensions):
            for d in self.dimensions:
                output+= u'[%d]' % (d[0])
        if len(self.initialization):
            output+= u' = '
            for i in self.initialization:
                output+= i.text();
        #output+= u';'
        return output

    def text_debug(self):
        print 'Variable:'
        print '    type: %s' % ( u' '.join([v.text() for v in self.var_type]) )
        print '    mods: %s' % ( u' '.join([m.text() for m in self.modifiers]) )
        print '    name: %s' % ( self.name )
        print '    dim:  [%d]' % ( len(self.dimensions) )
        print '    init: %s' % ( u' '.join([i.text() for i in self.initialization]) )

class Function(object):
    def __init__(self, namespace):
        self.namespace = namespace
        self.name = u''
        self.body = None
        self.ret_type = []
        self.arguments = []
        self.name_defined = False
        self.initializing = False
        self.initialization = []

    def parse(self, tokens):
        type_defined = False
        for t in tokens.byToken(True):
            if self.checkType(t):
                type_defined = True
                continue
            else:
                if not type_defined:
                    #print '%s: Undefined return type "%s"' % (
                    #        token.position(), token.text() )
                    return False
                if self.checkName(t):
                    tokens.move(1)
                    t = tokens.current()
                    if self.checkArguments(t):
                        tokens.move(1)
                        if self.checkBody(tokens):
                            return True
                    else:
                        #print u'func.checkArguments failed:', t.text()
                        return False
                else:
                    #print u'func.checkName failed:', t.text()
                    return False
        print u'No more tokens.', t.text()
        return False

    def checkArguments(self, exp):
        if (exp.node_type != 'node') or (exp.type != 'expression'):
            #print u'Function.checkArguments: exp of wrong type:'
            #print exp.text()
            #assert(1 == 0)
            return False
        args = []
        for t in exp.children.byToken():
            if t.text() not in u'()':
                args.append(t)
        args = self.splitBy(args, u',')
        for arg in args:
            v = self.checkVariable(arg)
            if v:
                self.arguments.append(v)
            else:
                #print u'checkArguments: Bad arg: "%s"' % (arg[0].text())
                return False
        return True

    def checkBody(self, tokens):
        t = tokens.current()
        if t.node_type == 'leaf' and t.text() == u';':
            return True
        else:
            if t.node_type == 'node' and t.type == 'namespace':
                self.body = t
                return True
            else:
                return False

    def checkVariable(self, arg):
        var = Variable(self.namespace)
        for t in arg:
            if not var.parse(t):
                if t.text() == u'...':
                    #del var
                    #var = Variable(self.namespace)
                    tokens = [Token(u'void'), Token(u'ANY_COUNT_OF_ARGUMENTS')]
                    origin = t.children.first().origin
                    for tt in tokens:
                        tt.setOrigin(origin)
                        var.parse(tt)
                else:
                    return False
        return var

    def splitBy(self, tokens, text):
        result = []
        current = []
        for t in tokens:
            if t.text() == text:
                result.append( current )
                current = []
            else:
                current.append(t)
        if len(current):
            result.append( current )
        return result

    def checkName(self, t):
        if (t.node_type != 'leaf') or (t.type != 'ident'):
            return False
        if (not self.namespace.isIdent(t)) or self.namespace.isType(t):
            return False
        self.name = t.text()
        return True

    def checkType(self, token):
        if (token.node_type != 'leaf') or (not self.namespace.isType(token)):
            return False
        self.ret_type.append( token )
        return True

    def text(self):
        output = u''
        for t in self.ret_type:
            output+= t.text() + u' '
        output+= self.name
        output+= u'( '
        for i, v in enumerate(self.arguments):
            if i != 0:
                output+= u', '
            output+= v.text()
        output+= u');'
        return output

class Struct(object):
    def __init__(self, namespace):
        self.name       = u''
        self.name_token = None
        self.members    = []
        self.namespace  = namespace
        self.local_ns   = None
        self.f_struct   = False
        self.f_name     = False
        self.f_content  = False

    def parse(self, tokens):
        for token in tokens.byToken(True):
            if self.f_content:
                self.__parseNew_type_variables(tokens)
                return
            elif self.f_struct:
                if self.namespace.isIdent(token):
                    self.name = token.text()
                    self.name_token = token
                    self.f_name = True
                elif token.node_type == 'node':
                    if token.type == 'namespace':
                        if not self.f_name:
                            self.name = self.namespace.getDefault_name()
                            self.name_token = Token(self.name, self.namespace)
                        self.local_ns = self.namespace.createChild(self.name)
                        self.__parseMembers(token)
                        self.namespace.addType(self)
                        self.f_content = True
                    else:
                        print 'Struct error: must be namespace', token.text()
                        return False
                else:
                    print 'Struct error: must be content or name.', token.text()
                    if token.node_type == 'leaf':
                        print u'%s: %s' % (token.position(), token.text())
                    elif token.node_type == 'node':
                        print u'%s' % (token.text())
                    return False
            elif token.text() == u'struct':
                self.f_struct = True
            else:
                print u'Struct error: must be "struct":', token.text()
                return False
        return True

    def __parseMembers(self, expression):
        somedef = SomeDefinition()
        cs = expression.children
        for t in cs.byToken():
            if (t.type == 'oper') and (t.text() in u'{};'): continue
            if t.type == 'ws': continue
            somedef.parse(cs, self.local_ns)
        for v in self.local_ns.variables:
            self.members.append(v)
        return

        member = Variable(self.local_ns)
        for t in expression.children.byToken():
            if t.node_type == 'leaf':
                if t.type == 'oper':
                    if t.text() in u'{}':
                        continue
                    if t.text() == u';':
                        self.members.append(member)
                        self.local_ns.addVariable(member)
                        member = Variable(self.local_ns)
                        continue
                    elif t.text() == u',':
                        self.members.append(member)
                        self.local_ns.addVariable(member)
                        old = member
                        member = Variable(self.local_ns)
                        for tt in self.var_type:
                            member.parse(tt)
                        continue
            if t.type != 'ws':
                member.parse(t)

    def __parseNew_type_variables(self, tokens):
        def createVar(type, namespace):
            var = Variable(namespace)
            var.parse(type)
            return var
        var = createVar(self.name_token, self.namespace)
        for token in tokens.byToken(True):
            if token.node_type == 'leaf':
                if token.text() == u',':
                    self.namespace.addVariable(var)
                    var = createVar(self.name_token, self.namespace)
                    continue
                elif token.text() == u';':
                    if var.name:
                        self.namespace.addVariable(var)
                    return True
            if not var.parse(token):
                return False

    def size(self):
        # Biggest member * member's count
        #biggest = 0
        #for m in self.members:
        #    biggest = max(biggest, m.size())
        #actual = biggest * len(self.members)
        #return actual
        actual = 0
        for m in self.members:
            actual+= m.size()
        return int(actual)

    def text(self):
        output = u''
        output+= u'struct '
        output+= u'%s(%d) {\n' % ( self.name, len(self.members) )
        for m in self.members:
            output+= u'    %s;\n' % (m.text())
        output+= u'}'
        return output

class Typedef():
    def __init__(self, namespace):
        self.name = None
        self.data = []
        self.namespace = namespace

    def parse(self, tokens):
        t = tokens.current()
        if t.text() == u'typedef':
            tokens.move(1)
            ns_name  = self.namespace.getDefault_name()
            local_ns = self.namespace.createChild(ns_name)
            sd = SomeDefinition()
            sd.parse(tokens, local_ns)
            for v in local_ns.variables + local_ns.types:
                v.namespace = self.namespace
                self.namespace.addType(v);
            self.namespace.removeChild(ns_name)
        else:
            print 'Typedef.parse error: "%s" != typedef:' % (t.text())
            return False

    def text(self):
        output = u''
        return output

class Enum(object):
    def __init__(self, namespace):
        self.name = u''
        self.name_token = None
        self.members = {}
        self.namespace = namespace
        self.local_ns = None
        self.f_enum = False
        self.f_name = False
        self.f_content = False
        self.mtype = Token(u'int')
        #self.mtype.setOrigin(u'global_namespace', 0)

    def parse(self, tokens):
        for token in tokens.byToken(True):
            if self.f_content:
                self.__parseNew_type_variables(tokens)
                return
            elif self.f_enum:
                if self.namespace.isIdent(token):
                    self.name = token.text()
                    self.name_token = token
                    self.f_name = True
                elif token.node_type == 'node':
                    if token.type == 'namespace':
                        if self.f_name:
                            self.local_ns = self.namespace.createChild(self.name)
                            self.__parseMembers(token)
                            self.namespace.addType(self)
                        else:
                            self.name = self.namespace.getDefault_name()
                            self.name_token = Token(self.name, self.namespace)
                            self.namespace.addType(self)
                            self.__parseMembers(token, True)
                        self.f_content = True
                    else:
                        print 'Struct error: must be namespace', token.text()
                        return False
                else:
                    print 'Enum error: must be content or name.', token.text()
                    if token.node_type == 'leaf':
                        print u'%s: %s' % (token.position(), token.text())
                    elif token.node_type == 'node':
                        print u'%s' % (token.text())
                    return False
            elif token.text() == u'enum':
                self.f_enum = True
            else:
                print u'Enum error: must be "enum":', token.text()
                return False
        return True

    def __parseMembers(self, expression, add_to_global = False):
        mvalue = 0
        members = expression.children.splitBy(u',')
        del members[0][0]   # {
        del members[-1][-1] # }
        for m in members:
            smvalue = u''
            mname   = u''
            f_value = False
            if add_to_global: member = Variable(self.namespace)
            else:             member = Variable(self.local_ns)
            member.parse(self.mtype)
            for t in m:
                if t.type != 'ws':
                    member.parse(t)
            ini = member.initialization
            if len(ini):
                mvalue = int(u' '.join([c.text() for c in ini]))
            else:
                mvalue+= 1
                member.parse(Token(u'=', self.namespace))
                member.parse(Token(unicode(mvalue), self.namespace))
            self.members[member.name] = mvalue
            if add_to_global: self.namespace.addConstant(member)
            else:             self.local_ns.addVariable(member)


    def __parseNew_type_variables(self, tokens):
        def createVar(type, namespace):
            var = Variable(namespace)
            var.parse(type)
            return var
        var = createVar(self.name_token, self.namespace)
        for token in tokens.byToken(True):
            if token.node_type == 'leaf':
                if token.text() == u',':
                    self.namespace.addVariable(var)
                    var = createVar(self.name_token, self.namespace)
                    continue
                elif token.text() == u';':
                    if var.name:
                        self.namespace.addVariable(var)
                    return True
            if not var.parse(token):
                return False

    def size(self):
        return self.namespace.type_sizes[u'int']

    def text(self):
        output = u''
        output+= u'enum '
        output+= u'%s(%d) {\n' % ( self.name, len(self.members) )
        for name, value in self.members.items():
            output+= u'    %s = %d,\n' % (name, value)
        output+= u'}'
        return output

class Type(object):
    def __init__(self, namespace):
        self.namespace = namespace
        self.tokens = []

    def parse(self, tokens):
        for token in tokens:
            if not self.parseOne(token):
                return False
        return True

    def parseOne(self, token):
        if self.namespace.isType(token):
            self.tokens.append(token)
            return True
        else:
            print u'Error: Type.parse() not type:'
            if token.node_type == 'leaf':
                print u'%s: "%s"' % (token.position(), token.text())
            elif token.node_type == 'node':
                print u'"%s"' % (token.text())
            return False

    def size(self):
        sizes = []
        for v in self.tokens:
            if hasattr(v, 'size'):
                sizes.append(v.size())
            else:
                sizes.append(self.namespace.getSize(v))
        if len(sizes):
            actual = reduce(max, sizes)
        else:
            print u'Error: Type.size() WTF:'
            print self.tokens
            actual = 0
        return actual

## Stop concatenation ##